﻿using System;
using System.Collections.Generic;

using System.Text;
using Google.GData.Calendar;
using Google.GData.Client;

namespace DGZfP.EventPublisher.Calendar
{
    public class EventsByGoogleCalendarFeed : IEnumerable<IEvent>
    {
        private const string STR_FeedUriPattern = "http://www.google.com/calendar/feeds/{0}/private/full";

        CalendarService Service { get; set; }
        EventFeed feed;
        Int32 batchCounter = 0;

        string NextBatchId
        {
            get
            {
                return string.Format("id{0}", batchCounter++);
            }
        }

        private EventFeed Feed
        {
            get
            {
                if (feed == null)
                {
                    Query.NumberToRetrieve = 1000;
                    feed = (EventFeed)Service.Query(Query);
                    FilterOnlyEventsInContext();
                }
                return feed;
            }
            set { feed = value; }
        }

        private void FilterOnlyEventsInContext()
        {
            for (int index = feed.Entries.Count - 1; index >= 0; index--)
            {
                EventEntry entry = (EventEntry)feed.Entries[index];
                if (new EventByGoogleCalendarEvent(entry).ContextId != Context)
                    feed.Entries.Remove(entry);
            }

        }

        EventQuery Query { get; set; }
        AtomFeed BatchFeed { get; set; }
        string Context { get; set; }

        public EventsByGoogleCalendarFeed(CalendarService Service, string FeedId)
        {
            this.Service = Service;
            Context = "";
            Query = new EventQuery(String.Format(STR_FeedUriPattern, FeedId));

        }

        public EventsByGoogleCalendarFeed(CalendarService Service, string FeedId, DateTime start, string Context)
        {
            this.Service = Service;

            Query = new EventQuery(String.Format(STR_FeedUriPattern, FeedId));
            Query.StartTime = start;
            this.Context = Context;
        }


        private void SynchroniseEvent(object sender, ListsCompareEventArgs<IEvent> e)
        {
            switch (e.Action)
            {
                case ListsCompareResult.ElementMissingInFirstList:
                    {
                        //Event needs to be deleted
                        EventEntry toDelete = ((EventByGoogleCalendarEvent)e.Element).EventEntry;
                        var slave = new EventEntry();
                        slave.Id = new AtomId(toDelete.EditUri.ToString());
                        slave.BatchData = new GDataBatchEntryData(NextBatchId, GDataBatchOperationType.delete);
                        BatchFeed.Entries.Add(slave);
                    }
                    break;
                case ListsCompareResult.ElementMissingInSecondList:
                    {
                        //Event needs to be inserted
                        var master = e.Element;
                        if (Context == master.ContextId)
                        {
                            var slave = new EventByGoogleCalendarEvent();
                            slave.Synchronize(master);
                            var toInsert = slave.EventEntry;
                            toInsert.BatchData = new GDataBatchEntryData(NextBatchId , GDataBatchOperationType.insert);
                            BatchFeed.Entries.Add(toInsert);
                        }
                    }
                    break;
                case ListsCompareResult.ElementIsInBothLists:
                    {
                        //Event needs to be tested for update
                        var master = e.Element;
                        if (Context == master.ContextId)
                        {
                            var slave = (EventByGoogleCalendarEvent)e.SecondElement;
                            slave.Synchronize(master);
                            if (slave.IsDirty)
                            {
                                var toUpdate = slave.EventEntry;
                                toUpdate.BatchData = new GDataBatchEntryData(NextBatchId , GDataBatchOperationType.update);
                                BatchFeed.Entries.Add(toUpdate);
                            }
                        }
                    }
                    break;
                default:
                    break;
            }
        }

     

        public void SynchroniseEvents(IEnumerable<IEvent> MasterEventlist)
        {
            //new BatchFeed
            BatchFeed = new AtomFeed(Feed);
            BatchFeed.Self = BatchFeed.Post;
            batchCounter=0;

            //Compare Lists
            var lc = new ListsComparer<IEvent>();
            lc.ListsCompareEvent += new ListsCompareEventHandler<IEvent>(SynchroniseEvent);
            lc.Compare(MasterEventlist, this,
                delegate(IEvent a, IEvent b)
                {
                    return
                        a.ID == b.ID
                        &&
                        a.ContextId == b.ContextId;
                });

            //Run Batch
            var batchResultFeed = (EventFeed)Service.Batch(BatchFeed, new Uri(Feed.Batch));

            //reset Feed
            Feed = null;

            //check the return values of the batch operations to make sure they all worked.
            //the insert operation should return a 201 and the rest should return 200
            foreach (EventEntry entry in batchResultFeed.Entries)
            {
                if (entry.BatchData.Status.Code != 200 && entry.BatchData.Status.Code != 201)
                {
                    throw new Exception("The batch operation with ID " + entry.BatchData.Id + " failed.");
                }
            }
        }


        #region IEnumerable<IEvent> Member

        public class MyEnumerator : IEnumerator<IEvent>
        {

            EventFeed Feed { get; set; }
            int index;
            public MyEnumerator(EventFeed Feed)
            {
                this.Feed = Feed;
                index = -1;
            }

            #region IEnumerator<IEvent> Member

            public IEvent Current
            {
                get { return new EventByGoogleCalendarEvent((EventEntry)Feed.Entries[index]); }
            }

            #endregion

            #region IDisposable Member

            public void Dispose()
            {
            }

            #endregion

            #region IEnumerator Member

            object System.Collections.IEnumerator.Current
            {
                get { return new EventByGoogleCalendarEvent((EventEntry)Feed.Entries[index]); }
            }

            public bool MoveNext()
            {
                index++;
                if (index >= Feed.Entries.Count) return false;
                return true;
            }

            public void Reset()
            {
                index = -1;
            }

            #endregion
        }

        public IEnumerator<IEvent> GetEnumerator()
        {
            return new MyEnumerator(Feed);
        }

        #endregion

        #region IEnumerable Member

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return new MyEnumerator(Feed);
        }

        #endregion

        public int Count
        {
            get
            {
                return (Feed.Entries.Count);
            }
        }


    }
}
